package poker;
import java.util.ArrayList;
import java.util.List;

/**
 * @author Taavet Tamm
 * @category poker app
 * @version %I% %G%
 * Main pokergame implementation class
 */
public class PokerGame {
	
	private List<PokerPlayer> players = new ArrayList<PokerPlayer>();
	private PokerDeck deck;
	private Card card1, card2, card3, card4, card5;
	private int personPosition;
	private int dealerPosition;
	private String gameState = "preflop";
	private int nextPlayer;	
	private int smallBlind;
	private int bigBlind;
	private int toCall;
	private int pot;
	private int playerCount;
	private int winnerPosition;
	
	public PokerGame(int playerCount, int startingScore, int person) {
		//init game variables
		this.smallBlind = 10;
		this.bigBlind = 20;
		this.playerCount = playerCount;
		this.toCall = this.bigBlind;
		this.personPosition = person;
		this.dealerPosition = (int)(10.0 * Math.random());
		if (this.dealerPosition == 7) this.nextPlayer = 0;
		else if (this.dealerPosition == 8) this.nextPlayer = 1;
		else if (this.dealerPosition == 9) this.nextPlayer = 2;
		else this.nextPlayer = this.dealerPosition + 3;
		//init players and deal cards
		this.deck = new PokerDeck();
		PokerPlayer player;
		players.clear();
		for (int i = 0; i < playerCount; i++) {
			player = new PokerPlayer("player"+1, startingScore);
			player.setCard1(this.deck.deal());
			player.setCard2(this.deck.deal());
			player.setPosition(i);
			player.setBet(0);
			player.setToCall(this.bigBlind);
			player.setStatus("waiting");
			players.add(player);			
		}
		//adjust scores to blinds		
		this.gameState = "Preflop";
		this.setBlinds();
	}
	
	public void nextRound() {
		this.toCall = this.bigBlind;
		this.dealerPosition = (this.dealerPosition == 9) ? 0 : this.dealerPosition +1;
		if (this.dealerPosition == 7) this.nextPlayer = 0;
		else if (this.dealerPosition == 8) this.nextPlayer = 1;
		else if (this.dealerPosition == 9) this.nextPlayer = 2;
		else this.nextPlayer = this.dealerPosition + 3;
		this.deck = new PokerDeck();
		PokerPlayer player;
		for (int i = 0; i < playerCount; i++) {
			player = this.players.get(i);
			player.setCard1(this.deck.deal());
			player.setCard2(this.deck.deal());			
			player.setBet(0);
			player.setToCall(this.bigBlind);
			player.setStatus("waiting");						
		}
		this.card1 = null;
		this.card2 = null;
		this.card3 = null;
		this.card4 = null;
		this.card5 = null;
		this.gameState = "Preflop";
		this.setBlinds();
	}
	
	private void setBlinds() {
		int small,big;
		if (this.dealerPosition == 9){
			small = 0;
			big = 1;
		} else if (this.dealerPosition == 8) {
			small = 1;
			big = 2;
		} else {
			big = this.dealerPosition + 2;
			small = this.dealerPosition + 1; 
		}
		this.players.get(small).setScore(this.players.get(small).getScore() - this.smallBlind);
		this.players.get(small).setStatus("waiting");
		this.players.get(big).setScore(this.players.get(big).getScore() - this.bigBlind);
		this.players.get(big).setStatus("waiting");
		this.players.get(small).setBet(this.smallBlind);
		this.players.get(big).setBet(this.bigBlind);
		this.pot = this.smallBlind + this.bigBlind;
	}
	
	public void nextMove() {
		if (this.gameState != "play") this.gameState = "play";
		if (this.nextPlayer == this.personPosition) {
			this.advance();
		} else {
			this.players.get(this.nextPlayer).doAction(this.dealerPosition,this.toCall);
			if (this.players.get(this.nextPlayer).getStatus() == "call") this.pot += this.bigBlind;
			this.advance();			
		}
	}
	public void recalculatePosition() {
		int startAt = 0;
		if (this.gameState == "flop" || this.gameState == "turn" || this.gameState == "river") {
			if (this.dealerPosition == 9)startAt = 0;
			else startAt = this.dealerPosition + 1;						
		} else {			
			if (this.dealerPosition == 7) startAt = 0;
			else if (this.dealerPosition == 8) startAt = 1;
			else if (this.dealerPosition == 9) startAt = 2;
			else startAt = this.dealerPosition + 3;
		}
		boolean found = false;
		for (int i=startAt;i<10;i++){
			if (this.players.get(i).getStatus() == "waiting") {
				this.nextPlayer = i;
				found = true;
				break;
			}
		}		
		if (!found) this.nextPlayer = startAt;
	}

	public void personCallAction() {
		this.pot += this.players.get(this.personPosition).getToCall();
		this.players.get(this.personPosition).doCallAction();
		this.advance();
	}
	
	public void personFoldAction() {
		this.players.get(this.personPosition).setStatus("fold");
		this.advance();
	}
	
	public void personRaiseAction() {	
		this.pot += this.players.get(this.personPosition).getToCall() + this.bigBlind;
		this.players.get(this.personPosition).doRaiseAction(this.bigBlind);
		for (int i = 0; i < this.playerCount; i++) {
			if (this.players.get(i).getStatus() != "fold") {
				this.players.get(i).setToCall(this.players.get(i).getToCall() + this.bigBlind);
				this.players.get(i).setStatus("waiting");
			}
		}
		this.players.get(this.personPosition).setStatus("raise");
		this.players.get(this.personPosition).setToCall(0);
		this.advance();
	}
	
	public void advance() {
		boolean isWaiting = false;
		for (int i = 0;i<this.playerCount;i++) {			
			if (this.players.get(i).getStatus() == "waiting") {
				isWaiting = true;
				break;
			}
		}
		if (isWaiting) {
			this.gameState = "play";
			while (isWaiting) {
				if (this.nextPlayer == 9 ) this.nextPlayer = 0;
				else this.nextPlayer = this.nextPlayer + 1;
				if (this.players.get(this.nextPlayer).getStatus() != "fold") isWaiting = false;
			}			
		} else {
			this.flop();
		}
	}
	
	public void flop() {
		if (this.checkForWinner()) return;
		if (this.card5 != null) {
			this.findWinner();
			return;
		}
		this.gameState = "flop";
		if (this.card4 != null)     this.card5 = this.deck.deal();
		else if (this.card3 != null)this.card4 = this.deck.deal();		
		else {			
			this.card1 = this.deck.deal();
			this.card2 = this.deck.deal();
			this.card3 = this.deck.deal();
		}
		for (int i = 0; i <  this.playerCount; i++) {
			if (this.players.get(i).getStatus() != "fold") this.players.get(i).setStatus("waiting");
		}
		this.recalculatePosition();
	}
	
	public boolean checkForWinner() {
		int count = 0;
		int winner_position = 0;
		for (int i = 0; i < this.playerCount; i++){
			if (this.players.get(i).getStatus() != "fold") {
				count++;
				winner_position = i;
			}
			if (count > 1) return false;
		}//by the end of for() I know that there is only one player left
		this.players.get(winner_position).setScore(this.players.get(winner_position).getScore()+this.pot);
		this.pot = 0;
		this.winnerPosition = winner_position;
		this.gameState = "over";
		return true;
	}
	
	private void findWinner() {
		if (this.checkForWinner()) return;
		//there is more than one player left
		for (int i = 0; i < this.playerCount; i++) {
			if (this.players.get(i).getStatus() != "fold") {
				this.players.get(i).findBestHand(this.card1, this.card2, this.card3, this.card4, this.card5);
				this.players.get(i).setStatus("loser");
			}
		}
		int strongest_hand = 0;
		int winner_position = 0;
		for (int i = 0; i < this.playerCount; i++) {
			if (this.players.get(i).getHandStrength() > strongest_hand) {				
				winner_position = i;
				strongest_hand = this.players.get(i).getHandStrength();
			}
		}
		this.players.get(winner_position).setScore(this.players.get(winner_position).getScore()+this.pot);
		this.players.get(winner_position).setStatus("winner");
		this.pot = 0;
		this.gameState = "over";
		this.winnerPosition = winner_position;
	}
	
	public boolean isPersonNext() {
		return (this.personPosition == this.nextPlayer);
	}
	
	public String getGameState() {
		return gameState;
	}

	public int getDealerPosition() {
		return dealerPosition;
	}
	public PokerPlayer getPlayer(int position) {
		return this.players.get(position);
	}	
	public int getPersonPosition() {
		return this.personPosition;
	}
	public int getNextPlayer() {
		return this.nextPlayer;
	}
	public int getPot() {
		return this.pot;
	}
	public Card getCard1() {
		return card1;
	}
	public Card getCard2() {
		return card2;
	}
	public Card getCard3() {
		return card3;
	}
	public Card getCard4() {
		return card4;
	}
	public Card getCard5() {
		return card5;
	}
	public int getPlayerCount() {
		return this.playerCount;
	}
	public int getBigBlind() {
		return this.bigBlind;
	}
	public int getWinnerPosition() {
		return this.winnerPosition;
	}
}
